home *** CD-ROM | disk | FTP | other *** search
/ Clickx 47 / Clickx 47.iso / assets / software / Miro_Installer.exe / Miro_Downloader.exe / util.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2008-01-10  |  24.6 KB  |  797 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. import os
  5. import random
  6. import re
  7. import sys
  8. import sha
  9. import time
  10. import string
  11. import urllib
  12. import socket
  13. import logging
  14. import filetypes
  15. import tempfile
  16. import threading
  17. import traceback
  18. import subprocess
  19. from clock import clock
  20. from types import UnicodeType, StringType
  21. chatter = True
  22. inDownloader = False
  23. ignoreErrors = False
  24.  
  25. def quoteJS(x):
  26.     x = x.replace('\\', '\\\\')
  27.     x = x.replace('"', '\\"')
  28.     x = x.replace("'", "\\'")
  29.     x = x.replace('\n', '\\n')
  30.     x = x.replace('\r', '\\r')
  31.     return x
  32.  
  33.  
  34. def getNiceStack():
  35.     """Get a stack trace that's a easier to read that the full one.  """
  36.     stack = traceback.extract_stack()
  37.     while (len(stack) > 0 or os.path.basename(stack[0][0]) == 'unittest.py' or isinstance(stack[0][3], str)) and stack[0][3].startswith('unittest.main'):
  38.         stack = stack[1:]
  39.     for i in xrange(len(stack)):
  40.         if os.path.basename(stack[i][0]) == 'util.py' and stack[i][2] in ('failed', 'failedExn'):
  41.             stack = stack[:i + 1]
  42.             break
  43.             continue
  44.     
  45.     stack = _[1]
  46.     return stack
  47.  
  48.  
  49. def readSimpleConfigFile(path):
  50.     ret = { }
  51.     f = open(path, 'rt')
  52.     for line in f.readlines():
  53.         if re.match('^[ \t]*$', line):
  54.             continue
  55.         
  56.         match = re.match('^([^ ]+) *= *([^\\r\\n]*)[\\r\\n]*$', line)
  57.         if not match:
  58.             print "WARNING: %s: ignored bad configuration directive '%s'" % (path, line)
  59.             continue
  60.         
  61.         key = match.group(1)
  62.         value = match.group(2)
  63.         if key in ret:
  64.             print "WARNING: %s: ignored duplicate directive '%s'" % (path, line)
  65.             continue
  66.         
  67.         ret[key] = value
  68.     
  69.     return ret
  70.  
  71.  
  72. def writeSimpleConfigFile(path, data):
  73.     f = open(path, 'wt')
  74.     for k, v in data.iteritems():
  75.         f.write('%s = %s\n' % (k, v))
  76.     
  77.     f.close()
  78.  
  79.  
  80. def queryRevision(f):
  81.     
  82.     try:
  83.         p = subprocess.Popen([
  84.             'svn',
  85.             'info',
  86.             f], stdout = subprocess.PIPE)
  87.         info = p.stdout.read()
  88.         p.stdout.close()
  89.         url = re.search('URL: (.*)', info).group(1)
  90.         url = url.strip()
  91.         revision = re.search('Revision: (.*)', info).group(1)
  92.         revision = revision.strip()
  93.         return (url, revision)
  94.     except KeyboardInterrupt:
  95.         raise 
  96.     except:
  97.         return None
  98.  
  99.  
  100.  
  101. def absolutePathToFileURL(path):
  102.     if isinstance(path, unicode):
  103.         path = path.encode('utf-8')
  104.     
  105.     parts = string.split(path, os.sep)
  106.     parts = [ urllib.quote(x, ':') for x in parts ]
  107.     return 'file://' + '/'.join(parts)
  108.  
  109.  
  110. def failedExn(when, **kwargs):
  111.     failed(when, withExn = True, **kwargs)
  112.  
  113.  
  114. def failed(when, withExn = False, details = None):
  115.     logging.info('failed() called; generating crash report.')
  116.     header = ''
  117.     
  118.     try:
  119.         import config
  120.         import prefs
  121.         header += 'App:        %s\n' % config.get(prefs.LONG_APP_NAME)
  122.         header += 'Publisher:  %s\n' % config.get(prefs.PUBLISHER)
  123.         header += 'Platform:   %s\n' % config.get(prefs.APP_PLATFORM)
  124.         header += 'Python:     %s\n' % sys.version.replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ')
  125.         header += 'Py Path:    %s\n' % repr(sys.path)
  126.         header += 'Version:    %s\n' % config.get(prefs.APP_VERSION)
  127.         header += 'Serial:     %s\n' % config.get(prefs.APP_SERIAL)
  128.         header += 'Revision:   %s\n' % config.get(prefs.APP_REVISION)
  129.         header += 'Builder:    %s\n' % config.get(prefs.BUILD_MACHINE)
  130.         header += 'Build Time: %s\n' % config.get(prefs.BUILD_TIME)
  131.     except KeyboardInterrupt:
  132.         raise 
  133.     except:
  134.         pass
  135.  
  136.     header += 'Time:       %s\n' % time.asctime()
  137.     header += 'When:       %s\n' % when
  138.     header += '\n'
  139.     if withExn:
  140.         header += 'Exception\n---------\n'
  141.         header += ''.join(traceback.format_exception(*sys.exc_info()))
  142.         header += '\n'
  143.     
  144.     if details:
  145.         header += 'Details: %s\n' % (details,)
  146.     
  147.     header += 'Call stack\n----------\n'
  148.     
  149.     try:
  150.         stack = getNiceStack()
  151.     except KeyboardInterrupt:
  152.         raise 
  153.     except:
  154.         stack = traceback.extract_stack()
  155.  
  156.     header += ''.join(traceback.format_list(stack))
  157.     header += '\n'
  158.     header += 'Threads\n-------\n'
  159.     header += 'Current: %s\n' % threading.currentThread().getName()
  160.     header += 'Active:\n'
  161.     for t in threading.enumerate():
  162.         if not t.isDaemon() or ' [Daemon]':
  163.             pass
  164.         header += ' - %s%s\n' % (t.getName(), '')
  165.     
  166.     report = '{{{\n%s}}}\n' % header
  167.     
  168.     def readLog(logFile, logName = 'Log'):
  169.         
  170.         try:
  171.             f = open(logFile, 'rt')
  172.             logContents = '%s\n---\n' % logName
  173.             logContents += f.read()
  174.             f.close()
  175.         except KeyboardInterrupt:
  176.             raise 
  177.         except:
  178.             logContents = ''
  179.  
  180.         return logContents
  181.  
  182.     logFile = config.get(prefs.LOG_PATHNAME)
  183.     downloaderLogFile = config.get(prefs.DOWNLOADER_LOG_PATHNAME)
  184.     if logFile is None:
  185.         logContents = 'No logfile available on this platform.\n'
  186.     else:
  187.         logContents = readLog(logFile)
  188.     if downloaderLogFile is not None:
  189.         if logContents is not None:
  190.             logContents += '\n' + readLog(downloaderLogFile, 'Downloader Log')
  191.         else:
  192.             logContents = readLog(downloaderLogFile)
  193.     
  194.     if logContents is not None:
  195.         report += '{{{\n%s}}}\n' % stringify(logContents)
  196.     
  197.     logging.info('----- CRASH REPORT (DANGER CAN HAPPEN) -----')
  198.     logging.info(header)
  199.     logging.info('----- END OF CRASH REPORT -----')
  200.     if not inDownloader:
  201.         
  202.         try:
  203.             import dialogs
  204.             _ = gettext
  205.             import gtcache
  206.             if not ignoreErrors:
  207.                 chkboxdialog = dialogs.CheckboxTextboxDialog(_('Internal Error'), _('Miro has encountered an internal error. You can help us track down this problem and fix it by submitting an error report.'), _('Include entire program database including all video and channel metadata with crash report'), False, _('Describe what you were doing that caused this error'), dialogs.BUTTON_SUBMIT_REPORT, dialogs.BUTTON_IGNORE)
  208.                 (chkboxdialog.run,)((lambda x: _sendReport(report, x)))
  209.         except Exception:
  210.             e = None
  211.             logging.exception('Execption when reporting errror..')
  212.         except:
  213.             None<EXCEPTION MATCH>Exception
  214.         
  215.  
  216.     None<EXCEPTION MATCH>Exception
  217.     command = command
  218.     daemon = daemon
  219.     import dl_daemon
  220.     c = command.DownloaderErrorCommand(daemon.lastDaemon, report)
  221.     c.send()
  222.  
  223.  
  224. def _sendReport(report, dialog):
  225.     global ignoreErrors
  226.     
  227.     def callback(result):
  228.         app.controller.sendingCrashReport -= 1
  229.         if result['status'] != 200 or result['body'] != 'OK':
  230.             logging.warning(u'Failed to submit crash report. Server returned %r' % result)
  231.         else:
  232.             logging.info(u'Crash report submitted successfully')
  233.  
  234.     
  235.     def errback(error):
  236.         app.controller.sendingCrashReport -= 1
  237.         logging.warning(u'Failed to submit crash report %r' % error)
  238.  
  239.     import dialogs
  240.     import httpclient
  241.     import config
  242.     import prefs
  243.     import app
  244.     if dialog.choice == dialogs.BUTTON_IGNORE:
  245.         ignoreErrors = True
  246.         return None
  247.     
  248.     backupfile = None
  249.     if hasattr(dialog, 'checkbox_value') and dialog.checkbox_value:
  250.         
  251.         try:
  252.             logging.info('Sending entire database')
  253.             import database
  254.             backupfile = database.defaultDatabase.liveStorage.backupDatabase()
  255.         traceback.print_exc()
  256.         logging.warning(u'Failed to backup database')
  257.  
  258.     
  259.     description = u'Description text not implemented'
  260.     if hasattr(dialog, 'textbox_value'):
  261.         description = dialog.textbox_value
  262.     
  263.     description = description.encode('utf-8')
  264.     postVars = {
  265.         'description': description,
  266.         'app_name': config.get(prefs.LONG_APP_NAME),
  267.         'log': report }
  268.     if backupfile:
  269.         postFiles = {
  270.             'databasebackup': {
  271.                 'filename': 'databasebackup.zip',
  272.                 'mimetype': 'application/octet-stream',
  273.                 'handle': open(backupfile, 'rb') } }
  274.     else:
  275.         postFiles = None
  276.     app.controller.sendingCrashReport += 1
  277.     httpclient.grabURL('http://participatoryculture.org/bogondeflector/index.php', callback, errback, method = 'POST', postVariables = postVars, postFiles = postFiles)
  278.  
  279.  
  280. class AutoflushingStream:
  281.     '''Converts a stream to an auto-flushing one.  It behaves in exactly the
  282.     same way, except all write() calls are automatically followed by a
  283.     flush().
  284.     '''
  285.     
  286.     def __init__(self, stream):
  287.         self.__dict__['stream'] = stream
  288.  
  289.     
  290.     def write(self, data):
  291.         if isinstance(data, unicode):
  292.             data = data.encode('ascii', 'backslashreplace')
  293.         
  294.         self.stream.write(data)
  295.         self.stream.flush()
  296.  
  297.     
  298.     def __getattr__(self, name):
  299.         return getattr(self.stream, name)
  300.  
  301.     
  302.     def __setattr__(self, name, value):
  303.         return setattr(self.stream, name, value)
  304.  
  305.  
  306.  
  307. def makeDummySocketPair():
  308.     '''Create a pair of sockets connected to each other on the local
  309.     interface.  Used to implement SocketHandler.wakeup().
  310.     '''
  311.     dummy_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  312.     dummy_server.bind(('127.0.0.1', 0))
  313.     dummy_server.listen(1)
  314.     server_address = dummy_server.getsockname()
  315.     first = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  316.     first.connect(server_address)
  317.     (second, address) = dummy_server.accept()
  318.     dummy_server.close()
  319.     return (first, second)
  320.  
  321.  
  322. def trapCall(when, function, *args, **kwargs):
  323.     '''Make a call to a function, but trap any exceptions and do a failedExn
  324.     call for them.  Return True if the function successfully completed, False
  325.     if it threw an exception
  326.     '''
  327.     
  328.     try:
  329.         function(*args, **kwargs)
  330.         return True
  331.     except KeyboardInterrupt:
  332.         raise 
  333.     except:
  334.         failedExn(when)
  335.         return False
  336.  
  337.  
  338. TRACK_CUMULATIVE = False
  339. cumulative = { }
  340. cancel = False
  341.  
  342. def timeTrapCall(when, function, *args, **kwargs):
  343.     global cancel, cancel
  344.     cancel = False
  345.     start = clock()
  346.     retval = trapCall(when, function, *args, **kwargs)
  347.     end = clock()
  348.     if cancel:
  349.         return retval
  350.     
  351.     if end - start > 1:
  352.         logging.timing('WARNING: %s too slow (%.3f secs)', when, end - start)
  353.     
  354.     if TRACK_CUMULATIVE:
  355.         
  356.         try:
  357.             total = cumulative[when]
  358.         except KeyboardInterrupt:
  359.             raise 
  360.         except:
  361.             total = 0
  362.  
  363.         total += end - start
  364.         cumulative[when] = total
  365.         return retval
  366.         if total > 5:
  367.             logging.timing('%s cumulative is too slow (%.3f secs)', when, total)
  368.             cumulative[when] = 0
  369.         
  370.     
  371.     cancel = True
  372.     return retval
  373.  
  374.  
  375. def getTorrentInfoHash(path):
  376.     import libtorrent as lt
  377.     f = open(path, 'rb')
  378.     
  379.     try:
  380.         data = f.read()
  381.         metainfo = lt.bdecode(data)
  382.         infohash = sha.sha(lt.bencode(metainfo['info'])).digest()
  383.         return infohash
  384.     finally:
  385.         f.close()
  386.  
  387.  
  388.  
  389. class ExponentialBackoffTracker:
  390.     '''Utility class to track exponential backoffs.'''
  391.     
  392.     def __init__(self, baseDelay):
  393.         self.baseDelay = self.currentDelay = baseDelay
  394.  
  395.     
  396.     def nextDelay(self):
  397.         rv = self.currentDelay
  398.         self.currentDelay *= 2
  399.         return rv
  400.  
  401.     
  402.     def reset(self):
  403.         self.currentDelay = self.baseDelay
  404.  
  405.  
  406.  
  407. def gatherVideos(path, progressCallback):
  408.     import item
  409.     import prefs
  410.     import config
  411.     import platformutils
  412.     keepGoing = True
  413.     parsed = 0
  414.     found = list()
  415.     
  416.     try:
  417.         for root, dirs, files in os.walk(path):
  418.             for f in files:
  419.                 parsed = parsed + 1
  420.                 if filetypes.isVideoFilename(f):
  421.                     found.append(os.path.join(root, f))
  422.                 
  423.                 if parsed > 1000:
  424.                     adjustedParsed = int(parsed / 100) * 100
  425.                 elif parsed > 100:
  426.                     adjustedParsed = int(parsed / 10) * 10
  427.                 else:
  428.                     adjustedParsed = parsed
  429.                 keepGoing = progressCallback(adjustedParsed, len(found))
  430.                 if not keepGoing:
  431.                     found = None
  432.                     raise 
  433.                     continue
  434.             
  435.             if config.get(prefs.SHORT_APP_NAME) in dirs:
  436.                 dirs.remove(config.get(prefs.SHORT_APP_NAME))
  437.                 continue
  438.     except KeyboardInterrupt:
  439.         raise 
  440.     except:
  441.         pass
  442.  
  443.     return found
  444.  
  445.  
  446. def formatSizeForUser(bytes, zeroString = '', withDecimals = True, kbOnly = False):
  447.     '''Format an int containing the number of bytes into a string suitable for
  448.     printing out to the user.  zeroString is the string to use if bytes == 0.
  449.     '''
  450.     _ = gettext
  451.     import gtcache
  452.     if bytes > 1073741824 and not kbOnly:
  453.         value = bytes / 1.07374e+09
  454.         if withDecimals:
  455.             format = _('%1.1fGB')
  456.         else:
  457.             format = _('%dGB')
  458.     elif bytes > 1048576 and not kbOnly:
  459.         value = bytes / 1.04858e+06
  460.         if withDecimals:
  461.             format = _('%1.1fMB')
  462.         else:
  463.             format = _('%dMB')
  464.     elif bytes > 1024:
  465.         value = bytes / 1024
  466.         if withDecimals:
  467.             format = _('%1.1fKB')
  468.         else:
  469.             format = _('%dKB')
  470.     elif bytes > 1:
  471.         value = bytes
  472.         if withDecimals:
  473.             format = _('%1.1fB')
  474.         else:
  475.             format = _('%dB')
  476.     else:
  477.         return zeroString
  478.     return format % value
  479.  
  480.  
  481. def formatTimeForUser(seconds, sign = 1):
  482.     """Format a duration in seconds into a string suitable for display, using
  483.     the minimum amount of digits. Negative durations used for remaining times
  484.     display a '-' sign.
  485.     """
  486.     (_, _, _, h, m, s, _, _, _) = time.gmtime(seconds)
  487.     if sign < 0:
  488.         sign = '-'
  489.     else:
  490.         sign = ''
  491.     if int(seconds) in range(0, 3600):
  492.         return '%s%d:%02u' % (sign, m, s)
  493.     else:
  494.         return '%s%d:%02u:%02u' % (sign, h, m, s)
  495.  
  496.  
  497. def makeAnchor(label, href):
  498.     return '<a href="%s">%s</a>' % (href, label)
  499.  
  500.  
  501. def makeEventURL(label, eventURL):
  502.     return '<a href="#" onclick="return eventURL(\'action:%s\');">%s</a>' % (eventURL, label)
  503.  
  504.  
  505. def clampText(text, maxLength):
  506.     if len(text) > maxLength:
  507.         return text[:maxLength - 3] + '...'
  508.     else:
  509.         return text
  510.  
  511.  
  512. def print_mem_usage(message):
  513.     pass
  514.  
  515.  
  516. class TooManySingletonsError(Exception):
  517.     pass
  518.  
  519.  
  520. def getSingletonDDBObject(view):
  521.     view.confirmDBThread()
  522.     viewLength = view.len()
  523.     if viewLength == 1:
  524.         view.resetCursor()
  525.         return view.next()
  526.     elif viewLength == 0:
  527.         raise LookupError("Can't find singleton in %s" % repr(view))
  528.     else:
  529.         msg = '%d objects in %s' % (viewLength, len(view))
  530.         raise TooManySingletonsError(msg)
  531.  
  532.  
  533. class ThreadSafeCounter:
  534.     '''Implements a counter that can be access by multiple threads.'''
  535.     
  536.     def __init__(self, initialValue = 0):
  537.         self.value = initialValue
  538.         self.lock = threading.Lock()
  539.  
  540.     
  541.     def inc(self):
  542.         self.lock.acquire()
  543.         
  544.         try:
  545.             self.value += 1
  546.         finally:
  547.             self.lock.release()
  548.  
  549.  
  550.     
  551.     def dec(self):
  552.         self.lock.acquire()
  553.         
  554.         try:
  555.             self.value -= 1
  556.         finally:
  557.             self.lock.release()
  558.  
  559.  
  560.     
  561.     def getvalue(self):
  562.         self.lock.acquire()
  563.         
  564.         try:
  565.             return self.value
  566.         finally:
  567.             self.lock.release()
  568.  
  569.  
  570.  
  571.  
  572. def setupLogging():
  573.     logging.addLevelName(25, 'TIMING')
  574.     
  575.     logging.timing = lambda msg, *args, **kargs: logging.log(25, msg, *args, **kargs)
  576.     logging.addLevelName(26, 'JSALERT')
  577.     
  578.     logging.jsalert = lambda msg, *args, **kargs: logging.log(26, msg, *args, **kargs)
  579.  
  580.  
  581. class DemocracyUnicodeError(StandardError):
  582.     pass
  583.  
  584.  
  585. def checkU(text):
  586.     if text is not None and type(text) != UnicodeType:
  587.         raise DemocracyUnicodeError, u'text "%s" is not a unicode string' % text
  588.     
  589.  
  590.  
  591. def returnsUnicode(func):
  592.     
  593.     def checkFunc(*args, **kwargs):
  594.         result = func(*args, **kwargs)
  595.         if result is not None:
  596.             checkU(result)
  597.         
  598.         return result
  599.  
  600.     return checkFunc
  601.  
  602.  
  603. def checkB(text):
  604.     if text is not None and type(text) != StringType:
  605.         raise DemocracyUnicodeError, u'text "%s" is not a binary string' % text
  606.     
  607.  
  608.  
  609. def returnsBinary(func):
  610.     
  611.     def checkFunc(*args, **kwargs):
  612.         result = func(*args, **kwargs)
  613.         if result is not None:
  614.             checkB(result)
  615.         
  616.         return result
  617.  
  618.     return checkFunc
  619.  
  620.  
  621. def checkURL(text):
  622.     if type(text) != UnicodeType:
  623.         raise DemocracyUnicodeError, u'url "%s" is not unicode' % text
  624.     
  625.     
  626.     try:
  627.         text.encode('ascii')
  628.     except:
  629.         raise DemocracyUnicodeError, u'url "%s" contains extended characters' % text
  630.  
  631.  
  632.  
  633. def returnsURL(func):
  634.     
  635.     def checkFunc(*args, **kwargs):
  636.         result = func(*args, **kwargs)
  637.         if result is not None:
  638.             checkURL(result)
  639.         
  640.         return result
  641.  
  642.     return checkFunc
  643.  
  644.  
  645. def checkF(text):
  646.     FilenameType = FilenameType
  647.     import platformutils
  648.     if text is not None and type(text) != FilenameType:
  649.         raise DemocracyUnicodeError, u'text "%s" is not a valid filename type' % text
  650.     
  651.  
  652.  
  653. def returnsFilename(func):
  654.     
  655.     def checkFunc(*args, **kwargs):
  656.         result = func(*args, **kwargs)
  657.         if result is not None:
  658.             checkF(result)
  659.         
  660.         return result
  661.  
  662.     return checkFunc
  663.  
  664.  
  665. def unicodify(d):
  666.     '''Turns all strings in data structure to unicode.
  667.     '''
  668.     if isinstance(d, dict):
  669.         for key in d.keys():
  670.             d[key] = unicodify(d[key])
  671.         
  672.     elif isinstance(d, list):
  673.         for key in range(len(d)):
  674.             d[key] = unicodify(d[key])
  675.         
  676.     elif type(d) == StringType:
  677.         d = d.decode('ascii', 'replace')
  678.     
  679.     return d
  680.  
  681.  
  682. def stringify(u, handleerror = 'xmlcharrefreplace'):
  683.     '''Takes a possibly unicode string and converts it to a string string.
  684.     This is required for some logging especially where the things being
  685.     logged are filenames which can be Unicode in the Windows platform.
  686.  
  687.     Note that this is not the inverse of unicodify.
  688.  
  689.     You can pass in a handleerror argument which defaults to "xmlcharrefreplace".
  690.     This will increase the string size as it converts unicode characters that
  691.     don\'t have ascii equivalents into escape sequences.  If you don\'t want to
  692.     increase the string length, use "replace" which will use ? for unicode
  693.     characters that don\'t have ascii equivalents.
  694.     '''
  695.     if isinstance(u, unicode):
  696.         return u.encode('ascii', handleerror)
  697.     
  698.     if not isinstance(u, str):
  699.         return str(u)
  700.     
  701.     return u
  702.  
  703.  
  704. def quoteUnicodeURL(url):
  705.     '''Quote international characters contained in a URL according to w3c, see:
  706.     <http://www.w3.org/International/O-URL-code.html>
  707.     '''
  708.     checkU(url)
  709.     quotedChars = list()
  710.     for c in url.encode('utf8'):
  711.         if ord(c) > 127:
  712.             quotedChars.append(urllib.quote(c))
  713.             continue
  714.         quotedChars.append(c)
  715.     
  716.     return u''.join(quotedChars)
  717.  
  718.  
  719. def no_console_startupinfo():
  720.     """Returns the startupinfo argument for subprocess.Popen so that we don't
  721.     open a console window.  On platforms other than windows, this is just
  722.     None.  On windows, it's some win32 sillyness.
  723.     """
  724.     if subprocess.mswindows:
  725.         startupinfo = subprocess.STARTUPINFO()
  726.         startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
  727.         return startupinfo
  728.     else:
  729.         return None
  730.  
  731.  
  732. def call_command(*args, **kwargs):
  733.     """Call an external command.  If the command doesn't exit with status 0,
  734.     or if it outputs to stderr, an exception will be raised.  Returns stdout.
  735.     """
  736.     ignore_stderr = kwargs.pop('ignore_stderr', False)
  737.     if kwargs:
  738.         raise TypeError('extra keyword arguments: %s' % kwargs)
  739.     
  740.     pipe = subprocess.Popen(args, stdout = subprocess.PIPE, stdin = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = no_console_startupinfo())
  741.     (stdout, stderr) = pipe.communicate()
  742.     if pipe.returncode != 0:
  743.         raise OSError('call_command with %s has return code %s\nstdout:%s\nstderr:%s' % (args, pipe.returncode, stdout, stderr))
  744.     elif stderr and not ignore_stderr:
  745.         raise OSError('call_command with %s outputed error text:\n%s' % (args, stderr))
  746.     else:
  747.         return stdout
  748.  
  749.  
  750. def getsize(path):
  751.     """Get the size of a path.  If it's a file, return the size of the file.
  752.     If it's a directory return the total size of all the files it contains.
  753.     """
  754.     if os.path.isdir(path):
  755.         size = 0
  756.         for dirpath, dirnames, filenames in os.walk(path):
  757.             for name in filenames:
  758.                 size += os.path.getsize(os.path.join(dirpath, name))
  759.             
  760.             size += os.path.getsize(dirpath)
  761.         
  762.         return size
  763.     else:
  764.         return os.path.getsize(path)
  765.  
  766.  
  767. def partition(list, size):
  768.     '''Partiction list into smaller lists such that none is larger than
  769.     size elements.
  770.  
  771.     Returns a list of lists.  The lists appended together will be the original
  772.     list.
  773.     '''
  774.     retval = []
  775.     for start in range(0, len(list), size):
  776.         retval.append(list[start:start + size])
  777.     
  778.     return retval
  779.  
  780.  
  781. def directoryWritable(directory):
  782.     '''Check if we can write to a directory.'''
  783.     
  784.     try:
  785.         f = tempfile.TemporaryFile(dir = directory)
  786.     except OSError:
  787.         return False
  788.  
  789.     f.close()
  790.     return True
  791.  
  792.  
  793. def random_string(length):
  794.     return ''.join((lambda .0: for i in .0:
  795. random.choice(string.ascii_letters))(xrange(length)))
  796.  
  797.